package pers.hll.aigc4chat.base.util;

import lombok.experimental.UtilityClass;
import pers.hll.aigc4chat.base.exception.BizException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 反射工具类
 *
 * @author hll
 * @since 2024/05/06
 */
@UtilityClass
public class ReflectUtil {

    private static final String GET = "get";

    private static final String IS = "is";

    private static final String SET = "set";

    /**
     * 将源对象{@code source}的非空字段的值赋给目标对象{@code target}
     * <p>{@code T}类上应有 {@link lombok.Data} 注解 或者 {@link lombok.Getter} 和 {@link lombok.Setter} 注解
     * 或者 使用IDEA自动生成的 getter和setter方法 不建议手动实现
     * 只处理{@code T}类里声明的方法，不包括从父类继承的方法
     *
     * @param target 目标对象
     * @param source 源对象
     * @param <T> 目标对象类型
     */
    public <T> void mergeNonNullFields(T target, T source) {
        if (source == null) {
            if (target != null) {
                return;
            }
            throw BizException.of("目标对象不能为空!");
        }
        Class<?> clazz = source.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if (isGetterMethod(method)) {
                String fieldName = getFieldName(method.getName());
                try {
                    Method setter = getSetterMethod(clazz, fieldName, method.getReturnType());
                    Object value = method.invoke(source);
                    if (value != null) {
                        setter.invoke(target, value);
                    }
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw BizException.of("字段值覆盖异常: ", e);
                }
            }
        }
    }

    /**
     * 判断是否是getter方法
     * <p>如果是以 {@code get} 或 {@code is} 开头 且 无入参 则这个方法是一个 getter 方法 否则不是
     *
     * @param method 方法
     * @return 是否是getter方法
     */
    private boolean isGetterMethod(Method method) {
        return (method.getName().startsWith(GET) || method.getName().startsWith(IS))
                && method.getParameterCount() == 0 ;
    }

    /**
     * 获取setter方法
     *
     * @param clazz 要获取方法的目标类
     * @param fieldName 字段名
     * @param parameterType 入参 因为是 setter方法 所以入参只有一个
     * @return setter方法
     */
    private Method getSetterMethod(Class<?> clazz, String fieldName, Class<?> parameterType) {
        try {
            return clazz.getMethod(SET + fieldName, parameterType);
        } catch (NoSuchMethodException e) {
            throw BizException.of("找不到对应的setter方法: {}{}", SET, fieldName, e);
        }
    }

    /**
     * 从getter方法名中提取字段名
     *
     * @param methodName getter方法名
     * @return 字段名
     */
    private String getFieldName(String methodName) {
        if (methodName.startsWith(GET)) {
            return methodName.substring(GET.length());
        }
        if (methodName.startsWith(IS)) {
            return methodName.substring(IS.length());
        }
        throw BizException.of("方法名不符合getter规范: {}", methodName);
    }
}
